package com.onpositive.knowledge.model.values;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import com.onpositive.data.IPropertyConstants;
import com.onpositive.inferencer.AbstractTheory;
import com.onpositive.inferencer.ValueMapTheory;
import com.onpositive.knowledge.model.value.audits.AbstractAuditResult;
import com.onpositive.knowledge.model.value.audits.GeneralAuditResult;
import com.onpositive.knowledge.model.value.audits.IdentValueSplitterAudit;
import com.onpositive.knowledge.model.value.audits.PredictionAudit;
import com.onpositive.knowledge.model.value.audits.PredictionAudit.TheoryAssembly;
import com.onpositive.knowledge.model.values.ReplacementProposals.Proposal;
import com.onpositive.semantic.model.api.property.IProperty;

public class PredictionTransformer extends AuditAssistedTransformer {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1902553913668055077L;
	public static final String ID = "prediction-transformer" ; 

	public PredictionTransformer(IHasAuditResult src) {
		super(src);
	}
	
	ThreadLocal<HashSet<IProperty>> propertiesSet =new ThreadLocal<HashSet<IProperty>>();

	@Override
	public DescribedValue[] adjust(Object object, IProperty prop, DescribedValue... original) {
		
		Boolean isService = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_IS_SERVICE_PROPERTY, Boolean.class, null) ;
		if( isService != null && isService )
			return original ;
		
		Boolean isDepricated = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_DEPRICATED_LABEL, Boolean.class, null);
		if( isDepricated != null && isDepricated )
			return original ;
		
		Boolean isIdent = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_IS_IDENTIFICATOR, Boolean.class, null);
		if( isIdent != null && !isIdent )
			return original ;
		
		boolean valueIsNonNull = valueIsNonNull(original);
		if( !valueIsNonNull )
			return original ;

		String name = prop.getMeta().getSingleValue( "id", String.class, null ) ;	
		
		AbstractAuditResult aResult = this.getAuditResultSource().getAuditResult() ;
		
		PredictionAudit.OwnAuditResult predictionResult = null ;
		
		if( aResult instanceof IdentValueSplitterAudit.OwnAuditResult )
			predictionResult = (PredictionAudit.OwnAuditResult)aResult ;

		else if( aResult instanceof GeneralAuditResult )
		{
			GeneralAuditResult gResult = (GeneralAuditResult) aResult ;
			AbstractAuditResult result = gResult.getResult( PredictionAudit.ID );
			if(  !(result instanceof PredictionAudit.OwnAuditResult))
				return original ;
			predictionResult = (PredictionAudit.OwnAuditResult) result ;			
		}
		
		if( predictionResult == null  )
			return original ;
		
		HashSet<IProperty> pSet = propertiesSet.get();
		if( pSet == null )
		{
			pSet = new HashSet<IProperty>() ;
			propertiesSet.set(pSet) ;
		}
		if( pSet.contains(prop) )
			return original ;
		
		pSet.add(prop) ;
		DescribedValue adjusted[] = doAdjust( prop, predictionResult, object );
		propertiesSet.get().remove(prop) ;
		
		return adjusted != null ? adjusted : original ;
	}

	public boolean valueIsNonNull( DescribedValue... original )
	{
		boolean valueIsNull = original == null || original.length == 0;
		if (!valueIsNull){
			boolean nn=true;
			l2:for ( IHasValue c:original ){
				while ( c instanceof IHasValue){
					Object value = ((IHasValue)c).value();
					if (value instanceof IHasValue){
						c=(IHasValue) value;
					}
					else{
						if (value!=null){
							nn=false;
							break l2;
						}
						else
							break;
					}
				}				
			}
			valueIsNull|=nn;
		}
		return valueIsNull ;
	}

	private DescribedValue[] doAdjust( IProperty prop, PredictionAudit.OwnAuditResult aResult, Object object) {
		
		String pName = prop.getId() ;
		PredictionAudit.PropertyResult pResult= aResult.getPropertyResult(pName) ;
		if(pResult == null)
			return null ;		
		
		ReplacementProposals proposals = pResult.getProposals() ;
		if( proposals.isEmpty() )
			return null ;
		
		Proposal proposal = proposals.getBestProposal(object) ;
		if( proposal == null )
			return null ;
		
		DescribedValue dVal = (DescribedValue) proposal.getVlaue() ;
		if( dVal == null )
			return null ;
		
		return new DescribedValue[]{ dVal } ;
	}

	@Override
	public String getId() {
		return ID;
	}

}
